package com.zj.mapping;

import com.zj.exception.ZJBatisException;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class TokenParser {
    private static String regex$="\\$\\{([^}])+\\}";
    private static String regex="#\\{([^}])+\\}";
    private static String sqlChar="?";
    private   List<Object> paramMappingList=new ArrayList<>();

    public List<Object> getParamMappingList() {
        return paramMappingList;
    }

    public  String parseSqlBinding(String str, Object parameterObject) {
        StringBuilder sb=new StringBuilder();
        try {
            char[] src = str.toCharArray();
            if (str == null || str.length() == 0) return str;
            Pattern pattern = Pattern.compile(regex$);
            Matcher matcher = pattern.matcher(str);
            int offset = 0;
            while (matcher.find()) {
                String str2 = matcher.group();
                int start = matcher.start();
                sb.append(src, offset, start - offset);
                sb.append(handleBinding(str2.substring(2, str2.length() - 1), parameterObject));
                offset = start + str2.length();
            }
            sb.append(str.substring(offset));
        }catch (Exception e){
            throw  new ZJBatisException(e);
        }
        return sb.toString();
    }

    public  String parseSqlAndGetParamMapppingList(String str,Object parameterObject){
        StringBuilder sb=new StringBuilder();
        try {
            char[] src=str.toCharArray();
            if (str==null||str.length()==0)return str;
            Pattern pattern = Pattern.compile(regex);
            Matcher matcher = pattern.matcher(str);
            int offset=0;
            while (matcher.find()) {
                String str2=matcher.group();
                int start=matcher.start();
                sb.append(src,offset,start-offset);
                sb.append(handleMapping(str2.substring(2,str2.length()-1),parameterObject));
                offset=start+str2.length();
            }
            sb.append(str.substring(offset));
        }catch (Exception e){
            throw  new ZJBatisException(e);
        }
        return sb.toString();
    }

    private  String handleBinding(String str ,Object param)throws Exception{
        Map<String,Object> paramMap=(Map<String,Object>)param;
        //如果是简单类型，即sql中的参数名就用的0，1，2等。
        if(paramMap.containsKey(str)){
            Object value=paramMap.get(str);
            return value==null?"":String.valueOf(value);
        }
        String[] splitParams=str.split("\\.");//取出参数绑定前缀来判断是入参的第几个参数。
        Object obj=paramMap.get(splitParams[0]);
        if(obj!=null){
            //如果是对象类型的属性，即sql中参数名用的0.xxx等
            Field field=obj.getClass().getDeclaredField(splitParams[1]);
            field.setAccessible(true); // 私有属性必须设置访问权限
            Object value=field.get(obj);
            return value==null?"":String.valueOf(value);
        }else{
            throw new Exception("sql绑定参数匹配错误");
        }
    }


    private  String handleMapping(String str ,Object param)throws Exception{
        Map<String,Object> paramMap=(Map<String,Object>)param;
        //如果是简单类型，即sql中的参数名就用的0，1，2等。
        if(paramMap.containsKey(str)){
            Object value=paramMap.get(str);
            paramMappingList.add(value);
            return "?";
        }
        String[] splitParams=str.split("\\.");//取出参数绑定前缀来判断是入参的第几个参数。
        Object obj=paramMap.get(splitParams[0]);
        if(obj!=null){
            //如果是对象类型的属性，即sql中参数名用的0.xxx等
            Field field=obj.getClass().getDeclaredField(splitParams[1]);
            field.setAccessible(true); // 私有属性必须设置访问权限
            Object value=field.get(obj);
            paramMappingList.add(value);
            return "?";
        }else{
            throw new Exception("sql绑定参数匹配错误");
        }
    }

    public static void main(String[] args){
        TokenParser parser=new TokenParser();
        String str="        update  user_inf set         ADDRESS=#{param1.address},        name=${param1.name}        where id=#{param2.id}\n";
        String res=parser.parseSqlBinding(str,new HashMap<>());
        System.out.println(res);

    }

}
